package entities.concept;

import java.util.HashSet;
import java.util.Set;

import option.KBVocabulary;
import option.QueryKeywords;

import domain.tc.TCNode.EdgeType;
import entities.KBEntity;
import entities.role.Role;

/**
 * A chain/feature/attribute only contains functional roles (if (a,b)(a,c)then
 * b=c). <br/>
 * In our case, features are simple ones, i.e., chain of length one We currently
 * allow f = k, f <= k and f < k as a concrete feature concept
 * 
 * @author J.W.Wu
 * 
 */
public abstract class DomainConcept<S, T> extends Concept {

	protected S f = null;
	protected T k = null;
	private final dcType tp;

	private static enum dcType {
		EQ, LT, LTEQ
	}

	public boolean hasUniversalInside(){
		return false;
	}
	public DomainConcept(S f, T k) {
		this.f = f;
		this.k = k;
		if (this instanceof DomainConceptEQ) {
			this.tp = dcType.EQ;
		} else if (this instanceof DomainConceptLT) {
			this.tp = dcType.LT;
		} else if (this instanceof DomainConceptLTEQ) {
			this.tp = dcType.LTEQ;
		} else {
			this.tp = null;
		}
	}
	
	//replace all old values in this concept by the new value
	public DomainConcept<?,?> replaceValue(Object old, Object now){
		Object newf = this.f, newk=this.k;
		if(this.f.equals(old)){
			newf = now;
		}
		if(this.k.equals(old)){
			newk = now;
		}
		switch (this.tp) {
		case EQ:
			return new DomainConceptEQ<Object, Object>(newf, newk);
		case LT:
			return new DomainConceptLT<Object, Object>(newf, newk);
		case LTEQ:
			return new DomainConceptLTEQ<Object, Object>(newf, newk);
		default:
			return this;
		}
	}

	@Override
	public Set<Role> extractFeatures() {
		Set<Role> fs = new HashSet<Role>();
		if (this.f instanceof Role) {
			fs.add((Role) f);
		}
		if (this.k instanceof Role) {
			fs.add((Role) k);
		}
		return fs;
	}

	@Override
	public Set<Role> extractRoles() {
		Set<Role> fs = new HashSet<Role>();
		return fs;
	}

	@Override
	public Set<String> extractConstants() {
		Set<String> ks = new HashSet<String>();
		if (f instanceof String) {
			ks.add((String) this.f);
		}
		if (this.k instanceof String) {
			ks.add((String) k);
		}
		return ks;
	}

	protected String getSerializedItem(Object fk) {
		if (fk instanceof Role) {
			return "(" + KBVocabulary.ROLE + " " + fk.toString() + ")";
		} else {
			return fk.toString();
		}
	}

	public EdgeType getOpType() {
		EdgeType op = null;
		if (this instanceof DomainConceptEQ) {
			op = EdgeType.EQ;
		} else if (this instanceof DomainConceptLTEQ) {
			op = EdgeType.LT_EQ;
		} else if (this instanceof DomainConceptLT) {
			op = EdgeType.LT;
		}
		return op;
	}

	public Object getRealFeature() {
		if (isLHSRole()) {
			return this.f;
		} else if (isRHSRole()) {
			return this.k;
		} else {
			System.err
					.println("No role in this domain concept, null returned: "
							+ this.toString());
			return null;
		}
	}

	public Object getRealValue() {
		if (isLHSString()) {
			return this.f;
		} else if (isRHSString()) {
			return this.k;
		} else {
			System.err
					.println("No string found in this domain concept, null returned: "
							+ this.toString());
			return null;
		}
	}

	public S getLHS() {
		return this.f;
	}

	public T getValue() {
		return this.k;
	}

	public boolean isLHSRole() {
		return this.f instanceof Role;
	}

	public boolean isRHSRole() {
		return this.k instanceof Role;
	}

	public boolean isLHSString() {
		return this.f instanceof String;
	}

	public boolean isRHSString() {
		return this.k instanceof String;
	}

//	private volatile int hashCode = 0;
	@Override
	public int hashCode() {
//		if (hashCode != 0) {
//			return hashCode;
//		}
		final int prime = 31;
		int result = 1;
		result = prime * result + ((f == null) ? 0 : f.hashCode());
		result = prime * result + ((k == null) ? 0 : k.hashCode());
//		hashCode = result;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (!(obj instanceof DomainConcept))
			return false;
		DomainConcept<?, ?> other = (DomainConcept<?, ?>) obj;
		if (f == null) {
			if (other.f != null)
				return false;
		} else if (!f.equals(other.f))
			return false;
		if (k == null) {
			if (other.k != null)
				return false;
		} else if (!k.equals(other.k))
			return false;
		return true;
	}

	@Override
	public Set<AtomicConcept> extracAtomicConcepts() {
		Set<AtomicConcept> cs = new HashSet<AtomicConcept>();
		return cs;
	}
	
	@Override 
	public Set<KBEntity> extractSig(){
		Set<KBEntity> sig = new HashSet<KBEntity>();
		return sig;
	}

	@Override
	public boolean isParameterized() {
		Object o = this.getRealValue();
		if (o != null && o instanceof String) {
			return ((String) o).equalsIgnoreCase(QueryKeywords.PARAMETER);
		}
		return true;
	}

	@Override
	public Concept makeParameterized() {
		Object lhs;
		String rhs = QueryKeywords.PARAMETER;
		if (f instanceof String && k instanceof String) {
			return this;
		} else if (f instanceof String) {
			lhs = k;
		} else if (k instanceof String) {
			lhs = f;
		} else {
			return this;
		}
		switch (this.tp) {
		case EQ:
			return new DomainConceptEQ<Object, String>(lhs, rhs);
		case LT:
			return new DomainConceptLT<Object, String>(lhs, rhs);
		case LTEQ:
			return new DomainConceptLTEQ<Object, String>(lhs, rhs);
		default:
			return this;
		}
	}

	@Override
	public Concept initParameters(Role given, String val) {
		if (!this.isParameterized()) {
			return this;
		}
		boolean replace = false;
		if (given.equals(f) || given.equals(k)) {
			replace = true;
		}
		if (replace) {
			switch (this.tp) {
			case EQ:
				return new DomainConceptEQ<Object, String>(given, val);
			case LT:
				return new DomainConceptLT<Object, String>(given, val);
			case LTEQ:
				return new DomainConceptLTEQ<Object, String>(given, val);
			default:
				return this;
			}
		} else {
			return this;
		}

	}
}
